﻿using BCCommon;
using BCCommon.OAEnums;
using BCCommon.OAEnums.Workflow;
using BCData.Common.Dictionary;
using BCData.OA.Workflow.WorkflowInstanceStepExecutionRecord;
using BCDto.OA.TeamWorks.TaskRecord;
using BCDto.OA.Workflow.WorkflowActivityInstance;
using BCDto.OA.Workflow.WorkflowActivityStepInstance;
using BCDto.OA.Workflow.WorkflowOperation;
using BCEntity.Common.Dictionary;
using BCEntity.OA.Workflow.WorkflowInstanceStepExecutionRecord;
using BCService.OA.TeamWorks.TaskRecordService;
using BCService.OA.Workflow.OAWorkflowFactoryInstance;
using BCService.OA.Workflow.WorkflowActivityInstanceBusiness;
using BCService.OA.Workflow.WorkflowFactoryInstance;
using BCService.OA.WorkflowEngine.WorkflowFactory;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace BCService.OA.WorkflowEngine.WorkflowRunning
{
    public class WorkflowRunningService : IWorkflowRunningService
    {
        private readonly IApplicationContextService applicationContextService;
        private readonly IWorkflowActivityInstanceBusinessService workflowActivityInstanceBusinessService;
        private readonly IWorkflowInstanceStepExecutionRecordData workflowInstanceStepExecutionRecordData;
        private readonly ITaskRecordService taskRecordService;
        private readonly IDictionaryReferenceData dictionaryReferenceData;
        private readonly IRedisService redisService;

        public WorkflowRunningService(IApplicationContextService applicationContextService,
            IWorkflowActivityInstanceBusinessService workflowActivityInstanceBusinessService,
            IWorkflowInstanceStepExecutionRecordData workflowInstanceStepExecutionRecordData,
            ITaskRecordService taskRecordService,
            IDictionaryReferenceData dictionaryReferenceData,
            IRedisService redisService)
        {
            this.applicationContextService = applicationContextService;
            this.workflowActivityInstanceBusinessService = workflowActivityInstanceBusinessService;
            this.workflowInstanceStepExecutionRecordData = workflowInstanceStepExecutionRecordData;
            this.taskRecordService = taskRecordService;
            this.dictionaryReferenceData = dictionaryReferenceData;
            this.redisService = redisService;
        }

        #region 开始运行
        public void StartRunning(long workflowActivityInstanceId)
        {
            var instanceDto = this.workflowActivityInstanceBusinessService.GetInstanceInfo(workflowActivityInstanceId);

            IWorkflowFactoryService workflowFactoryService = this.GetAssignService(instanceDto);

            if (workflowFactoryService != null)
            {
                workflowFactoryService.InitAssign(instanceDto);
            }
        }
        #endregion

        #region 指派
        public void Assigning(long workflowActivityInstanceId)
        {
            var instanceDto = this.workflowActivityInstanceBusinessService.GetInstanceInfo(workflowActivityInstanceId);
            var step = instanceDto.CurrentlyStep;
            var stepEntity = instanceDto.Steps.Single(p => p.TaskStep == step);
            if (stepEntity.AssignBased == AssignBasedType.UserDefined)
            {
                this.ExecuteActivityStepTaskMethod(instanceDto);
            }
            else
            {
                IWorkflowFactoryService workflowFactoryService = this.GetAssignService(instanceDto);
                workflowFactoryService.InitActivityStepInstanceAssign(workflowActivityInstanceId);
            }
        }
        #endregion

        #region 任务执行
        public void Execution(WorkflowOperationRequestDto workflowOperationRequestDto)
        {
            //1.获取工作流任务信息
            TaskRecordDto dto = this.taskRecordService.GetEntity(workflowOperationRequestDto.TaskRecordNo);
            //2.通过工作流任务信息获取工作流实例
            var instanceDto = this.workflowActivityInstanceBusinessService.GetInstanceInfo(dto.WorkflowActivityInstanceId);
            //3.
            int step = dto.CurrentlyStep.Value;
            var stepInstance = instanceDto.Steps.Single(p => p.TaskStep == step);
            if (!stepInstance.IsBaseHandleMode && !string.IsNullOrWhiteSpace(workflowOperationRequestDto.ExtendMethodCode))
            {
                if (instanceDto.ExtendMethods.Any(p => p.ExtendMethodCode == workflowOperationRequestDto.ExtendMethodCode))
                {
                    var extendMethodEntity = instanceDto.ExtendMethods.Single(p => p.ExtendMethodCode == workflowOperationRequestDto.ExtendMethodCode);
                    var result = applicationContextService.RequestServices.ExecuteMethod(extendMethodEntity.TaskServiceName, extendMethodEntity.TaskMethodName, dto.WorkflowActivityInstanceId);
                    WorkflowInstanceStepExecutionRecordEntity executionRecordEntity = new WorkflowInstanceStepExecutionRecordEntity
                    {
                        WorkflowActivityInstanceNo = instanceDto.WorkflowActivityInstanceNo,
                        TaskStep = dto.CurrentlyStep.Value,
                        TaskMethodTitle = extendMethodEntity.ExtendMethodTitle,
                        TaskServiceName = extendMethodEntity.TaskServiceName,
                        TaskMethodName = extendMethodEntity.ExtendMethodName,
                        TaskMethodParameters = JsonConvert.SerializeObject(new[] { dto.WorkflowActivityInstanceId }),
                        Result = JsonConvert.SerializeObject(result)
                    };
                    this.workflowInstanceStepExecutionRecordData.Add(executionRecordEntity);
                    if (this.taskRecordService.UpdateHandleStatus(dto.TaskRecordNo, OATaskHandleStatus.Handled))
                    {
                        IWorkflowFactoryService workflowFactoryService = this.GetAssignService(instanceDto);
                        workflowFactoryService.Pass(workflowOperationRequestDto);
                    }
                }
            }
            else
            {
                IWorkflowFactoryService workflowFactoryService = this.GetAssignService(instanceDto);
                switch (workflowOperationRequestDto.HandleStatus)
                {
                    case OATaskHandleOperationStatus.Handled:
                        workflowFactoryService.Pass(workflowOperationRequestDto);
                        break;
                    case OATaskHandleOperationStatus.Rejected:
                        workflowFactoryService.Reject(workflowOperationRequestDto);
                        break;
                    case OATaskHandleOperationStatus.Closed:
                        workflowFactoryService.Closed(workflowOperationRequestDto);
                        break;
                    default:
                        break;
                }
            }
        }
        #endregion

        #region 获取指派服务
        /// <summary>
        /// 获取指派服务
        /// </summary>
        /// <param name="workflowActivityInstanceDto">工作流活动实例</param>
        /// <returns></returns>
        private IWorkflowFactoryService GetAssignService(WorkflowActivityInstanceDto workflowActivityInstanceDto)
        {
            DictionaryReferenceEntity dictionaryReferenceEntity = this.redisService.TryGet(workflowActivityInstanceDto.WorkflowType, () =>
            {
                return this.dictionaryReferenceData.GetEntityAsync(workflowActivityInstanceDto.WorkflowType).GetAwaiter().GetResult();
            }, TimeSpan.FromDays(1));

            if (dictionaryReferenceEntity == null)
            {
                throw new ArgumentException("字典中缺少工作流类型，工作流类型:" + workflowActivityInstanceDto.WorkflowType);
            }

            IWorkflowFactoryService workflowAssignService = this.applicationContextService.RequestServices.GetService<IOAWorkflowFactoryInstanceService>();

            //switch (dictionaryReferenceEntity.TypeName)
            //{
            //    case "Finance":
            //        workflowAssignService = applicationContextService.RequestServices.GetService<IFinanceWorkflowFactoryInstanceService>();
            //        break;
            //    case "Attendance":
            //        workflowAssignService = applicationContextService.RequestServices.GetService<IAttendanceWorkflowFactoryInstanceService>();
            //        break;
            //    case "Logistics":
            //        workflowAssignService = applicationContextService.RequestServices.GetService<ILogisticsWorkflowFactoryInstanceService>();
            //        break;
            //    case "LogisticsFinance":
            //        workflowAssignService = applicationContextService.RequestServices.GetService<IFreightFinanceBusinessFactoryInstanceService>();
            //        break;
            //    case "OA":
            //        workflowAssignService = applicationContextService.RequestServices.GetService<IOAWorkflowFactoryInstanceService>();
            //        break;
            //    case "FreightFinance":
            //        workflowAssignService = applicationContextService.RequestServices.GetService<IFreightFinanceBusinessFactoryInstanceService>();
            //        break;
            //    default:
            //        workflowAssignService = null;
            //        break;
            //}

            return workflowAssignService;
        }

        #endregion

        #region 执行活动步骤实例扩展方法
        /// <summary>
        /// 执行活动步骤实例扩展方法
        /// </summary>
        /// <param name="instanceDto"></param>
        private void ExecuteActivityStepTaskMethod(WorkflowActivityInstanceDto instanceDto)
        {
            string resultString = string.Empty;
            string taskMethodTitle = string.Empty;
            string taskServiceName = string.Empty;
            string taskMethodName = string.Empty;
            string taskMethodParameters = string.Empty;

            int taskStep = 0;

            if (instanceDto.Steps != null && instanceDto.Steps.Any())
            {
                taskStep = instanceDto.CurrentlyStep;
                var stepInstance = instanceDto.Steps.Single(p => p.TaskStep == taskStep);
                if (!string.IsNullOrWhiteSpace(stepInstance.TaskMethodTitle))
                {
                    string parameters = stepInstance.TaskMethodParameters;
                    if (string.IsNullOrWhiteSpace(stepInstance.TaskMethodParameters))
                    {
                        Dictionary<string, object> dic = new Dictionary<string, object>
                        {
                            { "WorkflowActivityInstanceId", instanceDto.WorkflowActivityInstanceId }
                        };
                        parameters = JsonConvert.SerializeObject(dic);
                    }
                    object result = this.Running(stepInstance);
                    resultString = JsonConvert.SerializeObject(result);
                    taskMethodTitle = stepInstance.TaskMethodTitle;
                    taskServiceName = stepInstance.TaskServiceName;
                    taskMethodName = stepInstance.TaskMethodName;
                    taskMethodParameters = parameters;
                }
                WorkflowInstanceStepExecutionRecordEntity executionRecordEntity = new WorkflowInstanceStepExecutionRecordEntity
                {
                    WorkflowActivityInstanceNo = instanceDto.WorkflowActivityInstanceNo,
                    TaskStep = taskStep,
                    TaskMethodTitle = taskMethodTitle,
                    TaskServiceName = taskServiceName,
                    TaskMethodName = taskMethodName,
                    TaskMethodParameters = taskMethodParameters,
                    Result = resultString
                };
                this.workflowInstanceStepExecutionRecordData.Add(executionRecordEntity);
            }
        }
        #endregion

        #region 运行扩展方法
        /// <summary>
        /// 运行扩展方法
        /// </summary>
        /// <param name="dto">工作流步骤实例</param>
        /// <returns></returns>
        private object Running(WorkflowActivityStepInstanceDto dto)
        {
            try
            {
                Dictionary<string, object> dictionary = new Dictionary<string, object>();

                if (!string.IsNullOrWhiteSpace(dto.TaskMethodParameters))
                {
                    //string[] paramsTemplate = entity.TaskMethodParameters.Split(",");
                    //for (int i = 0; i < entity.TaskMethodParameters.Length; i++)
                    //{
                    //    string paramsName = paramsTemplate[i].Trim().Split(" ")[1].Trim();
                    //    dictionary.Add(paramsName, parameterArr[i]);
                    //}

                    dictionary = JsonConvert.DeserializeObject<Dictionary<string, object>>(dto.TaskMethodParameters);
                }

                //开始构建动态服务
                Type type = Type.GetType(dto.TaskServiceName);
                ConstructorInfo firstConstructor = type.GetTypeInfo().GetConstructors().FirstOrDefault();
                //获取构造参数列表最多的构造方法
                foreach (var constructor in type.GetTypeInfo().GetConstructors())
                {
                    if (constructor.GetParameters().Length > firstConstructor.GetParameters().Length)
                    {
                        firstConstructor = constructor;
                    }
                }
                //提取构造参数
                IEnumerable<object> constructorParameters = firstConstructor.GetParameters().Select(p => applicationContextService.RequestServices.GetService(p.ParameterType));
                //构建服务实例
                var service = Activator.CreateInstance(type, constructorParameters.ToArray());
                //构建服务方法
                MethodInfo method = type.GetMethod(dto.TaskMethodName);
                //提取服务方法调用参数列表
                ParameterInfo[] parameters = method.GetParameters();
                List<object> parameterArr = dictionary.Any() ? dictionary.Select(p => p.Value).ToList() : new List<object>();
                if (parameterArr.Count > 0)
                {
                    for (int i = 0; i < parameters.Length; i++)
                    {
                        parameterArr[i] = Convert.ChangeType(parameterArr[i], parameters[i].ParameterType);
                    }
                }
                else if (parameters.Any() && parameters.Count() == 1)
                {
                    parameterArr.Add(dto.WorkflowActivityInstanceId);
                }

                object result = method.Invoke(service, parameterArr.ToArray());
                return result;
            }
            catch (Exception ex)
            {
                //如果执行错误，将步骤回滚到上一步
                this.taskRecordService.UpdateRollBack(dto.WorkflowActivityInstanceId);
                throw new ArgumentException(ex.InnerException.Message);
            }
        }

        #endregion
    }
}
